﻿using System;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using O1;
using O1.Data;
using O1.Data.Traversal;

namespace Tests.O1.Data.Traversal
{
    [TestClass]
    public class CellBuilderTests
    {
        [TestMethod, TestCategory("Unit Test")]
        public void CellBuilderInitializesBufferAt256BytesByDefault()
        {
            new CellBuilder().GetBuffer().Length.Should().Be(256);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellBuilderToArrayReturnsNewTrimmedBufferInstance()
        {
            var subject = new CellBuilder();
            var buffer = subject.GetBuffer();
            var result = subject.ToArray();
            ReferenceEquals(buffer, result).Should().BeFalse();
            buffer.Length.Should().BeGreaterThan(result.Length);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellBuilderInitializesBufferAtProvidedCapacityByDefault()
        {
            new CellBuilder(192).GetBuffer().Length.Should().Be(192);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellBuilderEndEdgeWritesExpectedMarkerByte()
        {
            var subject = new CellBuilder();
            subject.EndEdge(ObservationInterests.Interested);
            subject.GetBuffer()[0].Should().Be(Markup.EdgeEnd);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellBuilderEndEdgeResetsPositionWhenNotInterested()
        {
            var subject = new CellBuilder();
            subject.StartVertex(new CellAddress(22, 7654));
            var expected = subject.Length;
            subject.StartEdge(new Edge(12, 55555, 22), EdgeDirections.Outbound);
            subject.Length.Should().BeGreaterThan(expected);
            subject.EndEdge(ObservationInterests.None);
            subject.Length.Should().Be(expected);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellBuilderEndVertexResetsPositionWhenNotInterested()
        {
            var subject = new CellBuilder();
            var expected = subject.Length;
            subject.StartVertex(new CellAddress(22, 7654));
            subject.StartEdge(new Edge(12, 55555, 22), EdgeDirections.Outbound);
            subject.Length.Should().BeGreaterThan(expected);
            subject.EndEdge(ObservationInterests.Interested);
            subject.EndVertex(ObservationInterests.None);
            subject.Length.Should().Be(expected);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellBuilderEvaluateWritesExpectedDataForDateProperty()
        {
            var subject = new CellBuilder();
            var expected = subject.Length + DateProperty.Size + 1;
            subject.Evaluate(new DateProperty());
            subject.GetBuffer()[0].Should().Be(Markup.DateProp);
            subject.Length.Should().Be(expected);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellBuilderEvaluateWritesExpectedDataForDateTimeProperty()
        {
            var subject = new CellBuilder();
            var expected = subject.Length + DateTimeProperty.Size + 1;
            subject.Evaluate(new DateTimeProperty());
            subject.GetBuffer()[0].Should().Be(Markup.DateTimeProp);
            subject.Length.Should().Be(expected);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellBuilderEvaluateWritesExpectedDataForDecimalProperty()
        {
            var subject = new CellBuilder();
            var expected = subject.Length + DecimalProperty.Size + 1;
            subject.Evaluate(new DecimalProperty());
            subject.GetBuffer()[0].Should().Be(Markup.DecimalProp);
            subject.Length.Should().Be(expected);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellBuilderEvaluateWritesExpectedDataForHashProperty()
        {
            var subject = new CellBuilder();
            var expected = subject.Length + HashProperty.Size + 1;
            subject.Evaluate(new HashProperty());
            subject.GetBuffer()[0].Should().Be(Markup.HashProp);
            subject.Length.Should().Be(expected);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellBuilderEvaluateWritesExpectedDataForInt16Property()
        {
            var subject = new CellBuilder();
            var expected = subject.Length + Int16Property.Size + 1;
            subject.Evaluate(new Int16Property());
            subject.GetBuffer()[0].Should().Be(Markup.Int16Prop);
            subject.Length.Should().Be(expected);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellBuilderEvaluateWritesExpectedDataForInt32Property()
        {
            var subject = new CellBuilder();
            var expected = subject.Length + Int32Property.Size + 1;
            subject.Evaluate(new Int32Property());
            subject.GetBuffer()[0].Should().Be(Markup.Int32Prop);
            subject.Length.Should().Be(expected);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellBuilderEvaluateWritesExpectedDataForInt64Property()
        {
            var subject = new CellBuilder();
            var expected = subject.Length + Int64Property.Size + 1;
            subject.Evaluate(new Int64Property());
            subject.GetBuffer()[0].Should().Be(Markup.Int64Prop);
            subject.Length.Should().Be(expected);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellBuilderEvaluateWritesExpectedDataForStringPropertyWithoutValue()
        {
            var subject = new CellBuilder();
            var expected = subject.Length + StringProperty.FixedSize + 1;
            subject.Evaluate(new StringProperty());
            subject.GetBuffer()[0].Should().Be(Markup.StringProp);
            subject.Length.Should().Be(expected);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellBuilderEvaluateWritesExpectedDataForStringPropertyWithValue()
        {
            var value = "TEST";
            var subject = new CellBuilder();
            var expected = subject.Length + StringProperty.FixedSize + 1 + value.Length;
            subject.Evaluate(StringProperty.Create(11, value));
            subject.GetBuffer()[0].Should().Be(Markup.StringProp);
            subject.Length.Should().Be(expected);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellBuilderLengthIsAsExpectedForCellWithEdgeAndProperties()
        {
            var builder = new CellBuilder();
            builder.StartVertex(new CellAddress(1, 7777));
            builder.Evaluate(new DateTimeProperty(20, DateTimeOffset.UtcNow));
            builder.StartEdge(new Edge(8, 88888, 1), EdgeDirections.Outbound);
            builder.Evaluate(new Int32Property(123, 876));
            builder.EndEdge(ObservationInterests.Interested);
            builder.EndVertex(ObservationInterests.Interested);

            const int expected = Sz.CLR.Int32 + DateTimeProperty.Size + 1 + Edge.Size + 1 + Int32Property.Size + 1 + 2;
            builder.Length.Should().Be(expected);
        }
    }
}
